import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
df = pd.read_csv('heart.csv')
df.head()
| sbp | tobacco | ldl | adiposity | famhist | typea | obesity | alcohol | age | chd | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 160 | 12.00 | 5.73 | 23.11 | Present | 49 | 25.30 | 97.20 | 52 | 1 |
| 1 | 144 | 0.01 | 4.41 | 28.61 | Absent | 55 | 28.87 | 2.06 | 63 | 1 |
| 2 | 118 | 0.08 | 3.48 | 32.28 | Present | 52 | 29.14 | 3.81 | 46 | 0 |
| 3 | 170 | 7.50 | 6.41 | 38.03 | Present | 51 | 31.99 | 24.26 | 58 | 1 |
| 4 | 134 | 13.60 | 3.50 | 27.78 | Present | 60 | 25.99 | 57.34 | 49 | 1 |
Grupa budująca sprawdziła już czy w zbiorze dnaych wsytępują braki danych - nie występują.
Zakodowali zmienną famhist, będącą zmienną binarną, więc my zrobimmy analogicznie:
df['famhist'] = df['famhist'].map({'Absent': 0, 'Present': 1})
Wykonamy taki sam podział zbioru a nastepnie będziemy analizować df_test.
df, df_test = train_test_split(df, test_size=0.3, random_state = 123)
W dalczej części, zbiór którym zajmowała się grupa "budująca" będziemy nazywali treninowym, a rozważany przez nas testowym.
Sprawdzimy czy rozkłady zmiennych w zbiorze testowym są takie jak w treningowym:
grid = sns.PairGrid(df_test)
grid = grid.map_upper(sns.kdeplot, fill=True, thresh=0.05)
grid = grid.map_diag(sns.histplot)
grid = grid.map_lower(sns.scatterplot)
grid.fig.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
Widać, że wszytskie zależności są niemal idenatyczne jak na zbiorze treningowym. Jedyna minimalna różnica jaką zaobserwowaliśmy to, że histrgram zmiennej adiposity na zbiorze treninowym trochę bardziej wskazywał na rozkład normalny niż w tym przypadku.
Następnie, zgodnie z poleceniem zadania domowego sprawdzimy korelacje przy użyciu biblioteki Seaborn.
mask = np.triu(np.ones_like(df_test.corr(), dtype=bool))
f, ax = plt.subplots(figsize=(8, 6))
cmap = sns.diverging_palette(230, 20, as_cmap=True)
sns.heatmap(df_test.corr(), mask=mask, cmap=cmap, center=0, square=True, linewidths=.5, cbar_kws={"shrink": .5})
plt.show()
Porównując z wykresem otrzymanym na zbiorze testowym w tym przypadku można zauważyć, że kolory są mniej intenstyne - czyli korelacje między zmiennymmi są słabsze. Zgadza się to, że wyraźnie odznacza się korelacja między adiposity a obesity, jednak dla zbioru treninowego niemal na tym samym poziomie była między adiposity a age. Tu także jest większa niz pozostałe ale na mniejszym poziomie. Widać także inne różnice: korelacja tobacco i sbp dla zbioru treninowego była dodatnia na poziomie około 0.2, a w tym przyapdku jest minimalnie ujemna.
pca = PCA()
pca.fit(df_test)
plt.figure(figsize=(8, 6))
plt.plot(range(10), np.cumsum(pca.explained_variance_ratio_), linestyle='-', marker='o')
plt.title("PCA cumulated explained variance ratio")
plt.xlabel("PC")
plt.ylabel("Explained Variance Ratio")
plt.show()
Wykres wygląda niemal ideantycznie jak dla zbioru testowego i zgadzamy się z wnioskiem, że wynika z niego, z zasady łokcia, że najlepiej rozważyć 3 lub 4 składowe.
pca = PCA(n_components=3)
components = pca.fit_transform(df_test)
total_var = pca.explained_variance_ratio_.sum() * 100
fig = px.scatter_3d(
components, x=0, y=1, z=2,
title=f'Total Explained Variance: {total_var:.2f}%',
labels={'0': 'PC 1', '1': 'PC 2', '2': 'PC 3'}
)
fig.show()
JEŚLI POWYŻSZY WYKRES SIĘ NIE WYŚWIETLA TO TRZEBA WYWOŁAĆ WSZYSTKIE KOMÓRKI W JUPYTER NOTEBOOK'U
W tym przypadku całkowita wyjaśniona warianacja różni się jedynie o 0.5. Wartości w na wykresie układają się podobnie, co było jednak rozłożymy na 2 wymiary, żeby lepiej dostrzec zależności.
grid = sns.PairGrid(pd.DataFrame(components))
grid = grid.map_upper(sns.kdeplot, fill=True, thresh=0.05)
grid = grid.map_diag(sns.histplot)
grid = grid.map_lower(sns.scatterplot)
grid.fig.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
Widać, że i w tym przypadku histogramy, scatterploty oraz wykresy kernel density estimate (KDE) są dośc podobne do tych otrzymanych na zbiorze testowym. Można jednak zauważyć, że PCA zadziałało trochę inaczej na przykładzie scatterplota, gdzie na osi pionowej są wartości kolumny 1, a na poziomej 0. Dla zbioru treninowego wszystkie wartości były powyżej prostej -x+25, a tu natomiast są poniżej prostej 2x-50 (odczytując mniej więcej z rysunku). Mimo tej zmiany widać jednak, że mają jakąś wyraźnie zaznaczoną granicę, co może wskazywac na pewne podobieństwo.
Mimo, że grupa budująca zauważyła, że najlepiej wybrać 3 lub 4 komponenty w PCA, to przetestowali jedynie jak to wygląda dla 3, więc my sprawdzimy jak przedstawia się to dla 4 zmiennych zarówno na zbiorze treninowym jak i testowym.
zbiór treninowy
pca = PCA(n_components=4)
components = pca.fit_transform(df)
grid = sns.PairGrid(pd.DataFrame(components))
grid = grid.map_upper(sns.kdeplot, fill=True, thresh=0.05)
grid = grid.map_diag(sns.histplot)
grid = grid.map_lower(sns.scatterplot)
grid.fig.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
Histogram dodatkowej zmiennej wzkazuje na rozkład normalny.
zbiór testowy
pca = PCA(n_components=4)
components = pca.fit_transform(df_test)
grid = sns.PairGrid(pd.DataFrame(components))
grid = grid.map_upper(sns.kdeplot, fill=True, thresh=0.05)
grid = grid.map_diag(sns.histplot)
grid = grid.map_lower(sns.scatterplot)
grid.fig.tight_layout()
plt.subplots_adjust(top=0.9)
plt.show()
Wszystkie zależności pozostają niemal takie same dla zbioru treninowego i testowego w przypakdu dodanej zmiennej.
pca = PCA(n_components=3)
components = pca.fit_transform(df_test)
#skalowanie, aby wykres był bardziej czytelny
x = components[:,0]
scale_x = 20.0/(x.max() - x.min())
y = components[:,1]
scale_y = 20.0/(y.max() - y.min())
data = np.vstack((x .T*scale_x,y .T*scale_y))
loadings = pca.components_ .T * np.sqrt(pca.explained_variance_)
fig = px.scatter(np.transpose(data), x=0,y=1)
for i, feature in enumerate(df_test.columns.to_list()):
fig.add_shape(
type='line',
x0=0, y0=0,
x1=loadings[i, 0],
y1=loadings[i, 1]
)
fig.add_annotation(
x=loadings[i, 0],
y=loadings[i, 1],
ax=0, ay=0,
xanchor="center",
yanchor="bottom",
text=feature,
)
fig.show()
JEŚLI POWYŻSZY WYKRES SIĘ NIE WYŚWIETLA TO TRZEBA WYWOŁAĆ WSZYSTKIE KOMÓRKI W JUPYTER NOTEBOOK'U
Z powyższego wykresu wynika, że sbp i alcohol są między sobą silnie skorelowane i że obie są raczej słabo skorelowane ze zmienną alcohol. Ponadto zmienna alcohol silnie wpływa na pierwszą składową PCA. Tak samo wynikało z wykresu na zbiorze treninowym, kktóry jedynie był w lustrzanym odbiciu względem poziomej osi.
Pozostałe zależności trudno zauważyć, ponieważ wariancje pozostałych zmiennych są znacznie mniejsze od wariancji trzech wspomnianych, dlatego tak samo jak poprzednia grupa wykonamy skalowanie danych przy pomocy StandardScaler'a i porównamy czy będą płynęły te same wnioski w tym przypadku.
df_test_scaled = StandardScaler().fit_transform(df_test)
df_test_scaled = pd.DataFrame(df_test_scaled, columns = df_test.columns)
pca = PCA(n_components=3)
components = pca.fit_transform(df_test_scaled)
#skalowanie, aby wykres był bardziej czytelny
x = components[:,0]
scale_x = 1.0/(x.max() - x.min())
y = components[:,1]
scale_y = 1.0/(y.max() - y.min())
data = np.vstack((x .T*scale_x,y .T*scale_y))
loadings = pca.components_ .T * np.sqrt(pca.explained_variance_)
fig = px.scatter(np.transpose(data), x=0,y=1)
for i, feature in enumerate(df_test_scaled.columns.to_list()):
fig.add_shape(
type='line',
x0=0, y0=0,
x1=loadings[i, 0],
y1=loadings[i, 1]
)
fig.add_annotation(
x=loadings[i, 0],
y=loadings[i, 1],
ax=0, ay=0,
xanchor="center",
yanchor="bottom",
text=feature,
)
fig.show()
JEŚLI POWYŻSZY WYKRES SIĘ NIE WYŚWIETLA TO TRZEBA WYWOŁAĆ WSZYSTKIE KOMÓRKI W JUPYTER NOTEBOOK'U
Także w tym przypadku wzajemne korelacje między zmiennymi dla zbioru treninowego i testowego się niewiele różnią.
Z wykresu korelacji wynikało, że istnieje duża między adiposity a obesity. Na tym wykresie też widać, że jest całkiem spora, jednak nie największa ze wszytskich. Wykres korelacji też wskazywał na silny związek adiposity i age ale i w tym przypadku nie jest to tak wyraźnie widoczne.
Tu z kolei odznacza się korelacja między age, famhist oraz chd. Widać też brak korelacji między np. obesity a alcohol. To akurat jest zgodne w wcześniejszym wykresem korelacji, bo i tam była niemal na zerowym poziomie.
Widać pewnien rozdźwięk między wykresem korelacji a korelacjami, które wynikają z wykresu biplot. Nie są to jednak dramtyczne różnice. Wnioski otrzymane dla zbioru treningowego przenoszą się na rozważamy przez nas zbiór testowy.